//A simple wrapper around libmagic
package magic

/*
#cgo LDFLAGS: -lmagic
#include <stdlib.h>
#include <magic.h>
*/
import "C"

import (
	"os"
	"strings"
	"unsafe"
)

const (
	NONE           = C.MAGIC_NONE
	DEBUG          = C.MAGIC_DEBUG
	SYMLINK        = C.MAGIC_SYMLINK
	COMPRESS       = C.MAGIC_COMPRESS
	DEVICES        = C.MAGIC_DEVICES
	MIME           = C.MAGIC_MIME
	CONTINUE       = C.MAGIC_CONTINUE
	CHECK          = C.MAGIC_CHECK
	PRESERVE_ATIME = C.MAGIC_PRESERVE_ATIME
	RAW            = C.MAGIC_RAW
	ERROR          = C.MAGIC_ERROR
)

type Db struct {
	m C.magic_t
}

func gstr(c *C.char) string {
	str := C.GoString(c)
	C.free(unsafe.Pointer(c))
	return str
}

var no_db = os.NewError("No magic number database")

func (m *Db) error() os.Error {
	if m == nil {
		return no_db
	}
	c := C.magic_error(m.m)
	if c == nil {
		return os.Errno(int64(C.magic_errno(m.m)))
	}
	return os.NewError(gstr(c))
}

//Open a magic number database, with the given flags. If no
//paths to alternate databases are specified, use the default database.
func New(flags int, dbs ...string) (*Db, os.Error) {
	m := &Db{C.magic_open(C.int(0))}

	e := C.magic_setflags(m.m, C.int(flags))
	if int(e) != 0 {
		return nil, m.error()
	}

	var csl *C.char
	if len(dbs) != 0 {
		csl = C.CString(strings.Join(dbs, ":"))
		defer C.free(unsafe.Pointer(csl))
	}

	e = C.magic_load(m.m, csl)
	if int(e) != 0 {
		return nil, m.error()
	}

	return m, nil
}

func (m *Db) Close() {
	if m == nil || m.m == nil {
		return
	}
	C.magic_close(m.m)
	m = nil
}

func (m *Db) destroy() {
	m.Close()
}

func (m *Db) ret(c *C.char) (string, os.Error) {
	if c == nil {
		return "", m.error()
	}
	return gstr(c), nil
}

//inspect filename
func (m *Db) File(filename string) (string, os.Error) {
	cstr := C.CString(filename)
	defer C.free(unsafe.Pointer(cstr))
	return m.ret(C.magic_file(m.m, cstr))
}

//inspect raw []byte
func (m *Db) Buffer(b []byte) (string, os.Error) {
	vp, ln := unsafe.Pointer(&b[0]), C.size_t(len(b))
	return m.ret(C.magic_buffer(m.m, vp, ln))
}
